Introduction

Segments split the ELF binary into parts which are then loaded into memory by the OS programme loader. They can be thought of as grouping sections by their attributes and only selecting those which will be loaded into memory. In essence, segments contain information needed at runtime, while sections contain information needed at link-time.

The Programme Header Table

Segments are described by programme headers which are stored in the Programme Header Table (PHT). These structs are again defined in <elf.h>:

typedef struct
{
  Elf32_Word	p_type;			/* Segment type */
  Elf32_Off	    p_offset;		/* Segment file offset */
  Elf32_Addr	p_vaddr;		/* Segment virtual address */
  Elf32_Addr	p_paddr;		/* Segment physical address */
  Elf32_Word	p_filesz;		/* Segment size in file */
  Elf32_Word	p_memsz;		/* Segment size in memory */
  Elf32_Word	p_flags;		/* Segment flags */
  Elf32_Word	p_align;		/* Segment alignment */
} Elf32_Phdr;

typedef struct
{
  Elf64_Word	p_type;			/* Segment type */
  Elf64_Word	p_flags;		/* Segment flags */
  Elf64_Off	    p_offset;		/* Segment file offset */
  Elf64_Addr	p_vaddr;		/* Segment virtual address */
  Elf64_Addr	p_paddr;		/* Segment physical address */
  Elf64_Xword	p_filesz;		/* Segment size in file */
  Elf64_Xword	p_memsz;		/* Segment size in memory */
  Elf64_Xword	p_align;		/* Segment alignment */
} Elf64_Phdr;

  • p_type - describes the type of the segment.
  • p_offset - the offset from the beginning of the file where the segment resides.
  • p_vaddr - the virtual address at which the segment resides in memory.
  • p_paddr - the segment's physical address, which is relevant only for systems with physical addressing. This member holds unspecified contents for executables and shared objects
  • p_filesz - the number of bytes the segment occupies in the file image. It may be 0.
  • p_memsz - the number of bytes the segment occupies in the memory image. It may be 0.
  • p_align - the value to which the segments are aligned in the file and in memory. If this holds 0 or 1, then no alignment is required. Otherwise, p_align should be a positive integer power of 2 and p_vaddr should be equal to p_offset % p_align.

The PHT can be viewed by specifying the -l argument to readelf:

Segment Types

NameValue
PT_NULL0
PT_LOAD1
PT_DYNAMIC2
PT_INTERP3
PT_NOTE4
PT_SHLIB5
PT_PHDR6
PT_TLS7
PT_LOOS0x60000000
PT_HIOS0x6fffffff
PT_LOPROC0x70000000
PT_HIPROC0x7fffffff

PT_LOAD

This specifies a loadable segment described by p_filesz and p_memsz which means the segment is going to be mapped into memory. Bytes from the file are mapped to the beginning of the memory segment. Should the memory size be larger than the file size, the extra bytes are filled with 0s and are placed after the segment's data. Note that the file size cannot be larger than the memory size.

Entries of this type are sorted in an ascending order in the PHT according to their p_vaddr field.

All executable files must contain at least one PT_LOAD segment.

PT_DYNAMIC

The dynamic segment is pertinent to executables which avail themselves of dynamic linking and contains information for the dynamic linker. It typically points to the .dynamic section and comprises a series of structures which hold the relevant information.

typedef struct
{
  Elf32_Sword	d_tag;			/* Dynamic entry type */
  union
    {
      Elf32_Word d_val;			/* Integer value */
      Elf32_Addr d_ptr;			/* Address value */
    } d_un;
} Elf32_Dyn;

typedef struct
{
  Elf64_Sxword	d_tag;			/* Dynamic entry type */
  union
    {
      Elf64_Xword d_val;		/* Integer value */
      Elf64_Addr d_ptr;			/* Address value */
    } d_un;
} Elf64_Dyn;

In essence, the d_tag field determines whether the d_un field is treated as a value or an address.

PT_NOTE

This segment is completely optional and may contain information that is pertinent to a specific system. It can hold a variable number of entries of size 4 or 8 bytes on 32-bit and 64-bit platforms, respectively.

PT_INTERP

Here are specified the location and size of a null terminated string which describes the programme interpreter. Only one such segment is allowed per file and it must also precede any PT_LOAD segments.

PT_PHDR

This segment contains the location and size of the Programme Header Table itself, both in the file and in memory. Similarly to PT_INTERP, only one such segment is allowed per file and it must also precede any PT_LOAD segments.

PT_TLS

This segment specifies the Thread-Local Storage template. The latter is an amalgamation of all sections of type SHF_TLS. TLS sections are used to specify the size and initial contents of data whose copies are to be associated with different threads of execution. The part of the TLS which holds this initialised data is referred to as the TLS initialisation image, while the rest of the template is comprised of one or more sections of type SHF_NOBITS.

MemberValue
p_offsetFile offset of the TLS initialization image
p_vaddrVirtual memory address of the TLS initialization image
p_paddrreserved
p_fileszSize of the TLS initialization image
p_memszTotal size of the TLS template
p_flagsPF_R
p_alignAlignment of the TLS template

Other Segments

PT_SHLIB is reserved but is unspecified, while values from PT_LOOS to PT_HIOS and from PT_LOPROC through PT_HIPROC are reserved for OS- and processor-specific semantics, respectively.

Segment Flags

The p_flags field describes the permissions the segment is equipped with. It is important to note that the system may actually give more access than requested with the exception that a segment will never be assigned write permissions, unless explicitly requested:

FlagsValueExactAllowable
none0All access deniedAll access denied
PF_X1Execute onlyRead, execute
PF_W2Write onlyRead, write, execute
PF_W+PF_X3Write, executeRead, write, execute
PF_R4Read onlyRead, execute
PF_R+PF_X5Read, executeRead, execute
PF_R+PF_W6Read, writeRead, write, execute
PF_R+PF_W+PF_X7Read, write, executeRead, write, execute